Conclusion
- imager tools can not count the cells
# load image
papSmear <- load.image('papSmear.jpg')
papSmear
Image. Width: 493 pix Height: 335 pix Depth: 1 Colour channels: 3
dim(papSmear)
[1] 493 335 1 3
par(mfrow=c(1,2))
plot(papSmear)
plot(grayscale(papSmear))

Depth = 1 means the object is a static image and not video.
Clour channels = 3 means the image content Red, Green, Blue colours.
#G(papSmear) <- 0
R(papSmear) <- 0
#B(papSmear) <- 0
plot(papSmear)

layout(t(1:2))
hist(papSmear)
hist(grayscale(papSmear))

hist function uses the values of the 3 channels rgb as a vector values. We can plot separately histograms by color
# for red
G(papSmear) %>% hist(main="Green channels values")

If we convert the image object’s to dataframe, we can view the value of each pixel.
layout(t(1:2))
head(as.data.frame(papSmear))
tail(as.data.frame(papSmear))
library(ggplot2)
papSeam_df <- as.data.frame(papSmear)
pap <- plyr::mutate(papSeam_df,channel=factor(cc,labels=c('R','G','B')))
ggplot(pap,aes(value,col=channel)) + geom_histogram(bins=30) + facet_wrap(~ channel)

papSmearg <- grayscale(papSmear)
f <- ecdf(papSmearg)
plot(f, main="Empirical CDF of luminance values (gray)")

gr <- imgradient(papSmear,"xy")
grG <- imgradient(grayscale(papSmear), "xy")
plot(gr, layout="row")

plot(grG, layout="row")

layout(t(1:2))
dx <- imgradient(papSmear,"x")
dy <- imgradient(papSmear,"y")
grad.mag <- sqrt(dx^2+dy^2)
plot(grad.mag,main="Gradient magnitude with color")
papSmear.G <- grayscale(papSmear)
dx.G <- imgradient(papSmear.G,"x")
dy.G <- imgradient(papSmear.G,"y")
grad.mag.G <- sqrt(dx.G^2+dy.G^2)
plot(grad.mag.G ,main="Gradient magnitude with gray")

l <- imgradient(papSmear,"x")
head(as.data.frame(l))
dim(l)
[1] 493 335 1 3
par(mfrow=c(1,2))
plot(imhessian(papSmear)[2])

plot(with(imhessian(papSmear),(xx*yy - xy^2)), main="Determinant of Hessian")

layout(t(1:2))
threshold(papSmear,"99%") %>% plot(main="Determinant: 1% highest values (raw) ")
threshold(with(imhessian(papSmear),(xx*yy - xy^2)),"99%") %>% plot(main="Determinant: 1% highest values")

threshold(grad.mag, "99%") %>% plot(main="Determinant: 1% highest values (gradient)")

lab1 <- threshold(grad.mag, "99%") %>% label
lab2 <- threshold(with(imhessian(papSmear),(xx*yy - xy^2)), "99%") %>% label
par(mfrow=c(1,2))
plot(lab1, main= "Labelled regions from gradient")
plot(lab2, main= "Labelled regions from scare")

class(lab1)
[1] "cimg" "imager_array" "numeric"
lab1
Image. Width: 493 pix Height: 335 pix Depth: 1 Colour channels: 3
df1 <- as.data.frame(lab1) %>% subset(value>0)
df2 <- as.data.frame(lab2) %>% subset(value>0)
head(df2,3)
par(mfrow=c(1,2))
centers1 <- dplyr::group_by(df1,value) %>% dplyr::summarise(mx=mean(x),my=mean(y))
plot(lab1)
with(centers1, points(mx, my, col="yellow"))
centers2 <- dplyr::group_by(df2,value) %>% dplyr::summarise(mx=mean(x),my=mean(y))
plot(lab2)
with(centers2, points(mx, my, col="red"))

get.centers <- function(im,thr="99%")
{ im <- isoblur(im,5)
dt <- imhessian(im) %$% { xx*yy - xy^2 } %>% threshold(thr) %>% label
as.data.frame(dt) %>% subset(value>0) %>% dplyr::group_by(value) %>% dplyr::summarise(mx=mean(x),my=mean(y))
}
par(mfrow=c(1,2))
plot(lab1, main ="")
get.centers(lab1,"50%") %$% points(mx,my,col="yellow")
plot(grad.mag,main="Gradient magnitude with color")

Conclusion
Cette méthode ne permet pas de compter les cellules.
Faire des essays avec Pixsets
im <- load.image("papSmear.jpg")
im <- grayscale(im)
px <- im > .6
par(mfrow=c(1,2))
plot(im, main = "raw image")
plot(px, main = ">0.6 intensity")

dim(px)
[1] 493 335 1 1
dim(im)
[1] 493 335 1 1
mean(px)
[1] 0.8716418
mean(im)
[1] 0.7699147
mean(im[px])
[1] 0.8081526
par(mfrow=c(1,2))
plot(im , main = "im")
#highlight(im)
px <- isoblur(im, 1) > .6
plot(px, main ="px")
highlight(px)

par(mfrow=c(1,2))
plot(im)
px.flood(im,278,300,sigma=.31) %>% highlight

sp <- split_connected(px)
plot(sp[1:4])

par(mfrow=c(1,2))
boundary(px) %>% plot(main = "px <- isoblur(im, 1) > .6")
boundary(im) %>% plot( main= "im")

par(mfrow=c(1,2))
plot(im, main= "im with boundary")
boundary(px) %>% where %$% { points(x,y,cex=.1,col="red") }
plot(im, main="im with highlight(px)")
highlight(px)

################
#par(mfrow=c(1,2))
plot(im, main="with shink")
highlight(px)
#Shrink by 5 pixels
shrink(px,5) %>% highlight(col="blue")
#Grow by 5 pixels
grow(px,5) %>% highlight(col="green")

#Compute bounding box
bbox(px) %>% highlight(col="yellow")
all z values are equal
px.none(im) #No pixels
Pixel set of size 0. Width: 493 pix Height: 335 pix Depth: 1 Colour channels: 1
px.all(im) #All of them
Pixel set of size 165155. Width: 493 pix Height: 335 pix Depth: 1 Colour channels: 1
plot(im)
#Image borders at depth 10
px.borders(im,10) %>% highlight
#Left-hand border (5 pixels), see also px.top, px.bottom, etc.
px.left(im,5) %>% highlight(col="green")

#Split pixset in two along x
imsplit(im,"x",2) %>% plot(layout="row")

plot(im > .6)

# Splitting by color
imc <- load.image("papSmear.jpg")
imsplit(imc,"c") %>% plot

plot(imc)
imsplit(imc > 0.75,"c") %>% parany %>% highlight

threshold(imc) %>% plot

library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:plyr’:
arrange, count, desc, failwith, id, mutate, rename, summarise, summarize
The following object is masked from ‘package:EBImage’:
combine
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
d <- as.data.frame(im)
##Subsamble, fit a linear model
m <- sample_n(d,1e4) %>% lm(value ~ x*y,data=.)
##Correct by removing the trend
im.c <- im-predict(m,d)
out <- threshold(im.c)
plot(out)

out <- clean(out,3) %>% imager::fill(7)
plot(im)
highlight(out)

bg <- (!threshold(im.c,"10%"))
fg <- (threshold(im.c,"90%"))
imlist(fg,bg) %>% plot(layout="row")

LS0tCnRpdGxlOiBpbWFnZXIgZm9yIGN5dG9wYXRob2xvZ3kKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAotLS0KCkwnb2plY3RpZiBlc3QgZGUgc2F2b2lyIHNpIGF2ZWMgY2VyYXRpbmVzIGZvbmN0aW9uIGRlIGBpbWFnZXJgLCBlc3QgcG9zc2libGUgZGUgY29tcHRlciBsZXMgY2VsbHVsZXMgCgojIENvbmNsdXNpb24KLSBpbWFnZXIgdG9vbHMgY2FuIG5vdCBjb3VudCB0aGUgY2VsbHMKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaW1hZ2VyKQpgYGAKCgpgYGB7ciBlY2hvPVRSVUV9CiMgbG9hZCBpbWFnZQpwYXBTbWVhciA8LSBsb2FkLmltYWdlKCdwYXBTbWVhci5qcGcnKQpwYXBTbWVhcgpkaW0ocGFwU21lYXIpCnBhcihtZnJvdz1jKDEsMikpCnBsb3QocGFwU21lYXIpCnBsb3QoZ3JheXNjYWxlKHBhcFNtZWFyKSkKYGBgCkRlcHRoID0gMSBtZWFucyB0aGUgb2JqZWN0IGlzIGEgc3RhdGljIGltYWdlIGFuZCBub3QgdmlkZW8uIDxiciAvPgpDbG91ciBjaGFubmVscyA9IDMgbWVhbnMgdGhlIGltYWdlIGNvbnRlbnQgUmVkLCBHcmVlbiwgQmx1ZSBjb2xvdXJzLgoKYGBge3J9CiNHKHBhcFNtZWFyKSA8LSAwClIocGFwU21lYXIpIDwtIDAKI0IocGFwU21lYXIpIDwtIDAKcGxvdChwYXBTbWVhcikKYGBgCgoKCgoKYGBge3J9CmxheW91dCh0KDE6MikpCmhpc3QocGFwU21lYXIpCmhpc3QoZ3JheXNjYWxlKHBhcFNtZWFyKSkKYGBgCgpgYGBoaXN0YGBgIGZ1bmN0aW9uIHVzZXMgdGhlIHZhbHVlcyBvZiB0aGUgMyBjaGFubmVscyByZ2IgYXMgYSB2ZWN0b3IgdmFsdWVzLiBXZSBjYW4gcGxvdCBzZXBhcmF0ZWx5IGhpc3RvZ3JhbXMgYnkgY29sb3IKCmBgYHtyfQojIGZvciByZWQKRyhwYXBTbWVhcikgJT4lIGhpc3QobWFpbj0iR3JlZW4gY2hhbm5lbHMgdmFsdWVzIikKYGBgCgpJZiB3ZSBjb252ZXJ0IHRoZSBpbWFnZSBvYmplY3QncyB0byBkYXRhZnJhbWUsIHdlIGNhbiB2aWV3IHRoZSB2YWx1ZSBvZiBlYWNoIHBpeGVsLgoKYGBge3J9CmxheW91dCh0KDE6MikpCmhlYWQoYXMuZGF0YS5mcmFtZShwYXBTbWVhcikpCnRhaWwoYXMuZGF0YS5mcmFtZShwYXBTbWVhcikpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCnBhcFNlYW1fZGYgPC0gYXMuZGF0YS5mcmFtZShwYXBTbWVhcikKcGFwIDwtIHBseXI6Om11dGF0ZShwYXBTZWFtX2RmLGNoYW5uZWw9ZmFjdG9yKGNjLGxhYmVscz1jKCdSJywnRycsJ0InKSkpCmdncGxvdChwYXAsYWVzKHZhbHVlLGNvbD1jaGFubmVsKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwKSArIGZhY2V0X3dyYXAofiBjaGFubmVsKQpgYGAKCgoKYGBge3IgZWNobz1UUlVFfQoKcGFwU21lYXJnIDwtIGdyYXlzY2FsZShwYXBTbWVhcikKZiA8LSBlY2RmKHBhcFNtZWFyZykKcGxvdChmLCBtYWluPSJFbXBpcmljYWwgQ0RGIG9mIGx1bWluYW5jZSB2YWx1ZXMgKGdyYXkpIikKCmBgYAoKCmBgYHtyIGVjaG89VFJVRX0KCmdyIDwtIGltZ3JhZGllbnQocGFwU21lYXIsInh5IikKZ3JHIDwtIGltZ3JhZGllbnQoZ3JheXNjYWxlKHBhcFNtZWFyKSwgInh5IikKCnBsb3QoZ3IsIGxheW91dD0icm93IikKcGxvdChnckcsIGxheW91dD0icm93IikKYGBgCgoKCmBgYHtyIGVjaG89VFJVRX0KbGF5b3V0KHQoMToyKSkKZHggPC0gaW1ncmFkaWVudChwYXBTbWVhciwieCIpCmR5IDwtIGltZ3JhZGllbnQocGFwU21lYXIsInkiKQpncmFkLm1hZyA8LSBzcXJ0KGR4XjIrZHleMikKcGxvdChncmFkLm1hZyxtYWluPSJHcmFkaWVudCBtYWduaXR1ZGUgd2l0aCBjb2xvciIpCgpwYXBTbWVhci5HIDwtIGdyYXlzY2FsZShwYXBTbWVhcikKZHguRyA8LSBpbWdyYWRpZW50KHBhcFNtZWFyLkcsIngiKQpkeS5HIDwtIGltZ3JhZGllbnQocGFwU21lYXIuRywieSIpCmdyYWQubWFnLkcgPC0gc3FydChkeC5HXjIrZHkuR14yKQpwbG90KGdyYWQubWFnLkcgLG1haW49IkdyYWRpZW50IG1hZ25pdHVkZSB3aXRoIGdyYXkiKQpgYGAKCmBgYHtyIGVjaG89VFJVRX0KbCA8LSBpbWdyYWRpZW50KHBhcFNtZWFyLCJ4IikKaGVhZChhcy5kYXRhLmZyYW1lKGwpKQpkaW0obCkKYGBgCgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChpbWhlc3NpYW4ocGFwU21lYXIpWzJdKQpwbG90KHdpdGgoaW1oZXNzaWFuKHBhcFNtZWFyKSwoeHgqeXkgLSB4eV4yKSksIG1haW49IkRldGVybWluYW50IG9mIEhlc3NpYW4iKQpgYGAKCmBgYHtyfQpsYXlvdXQodCgxOjIpKQp0aHJlc2hvbGQocGFwU21lYXIsIjk5JSIpICU+JSBwbG90KG1haW49IkRldGVybWluYW50OiAxJSBoaWdoZXN0IHZhbHVlcyAocmF3KSAiKQp0aHJlc2hvbGQod2l0aChpbWhlc3NpYW4ocGFwU21lYXIpLCh4eCp5eSAtIHh5XjIpKSwiOTklIikgJT4lIHBsb3QobWFpbj0iRGV0ZXJtaW5hbnQ6IDElIGhpZ2hlc3QgdmFsdWVzIikKCmBgYApgYGB7cn0KdGhyZXNob2xkKGdyYWQubWFnLCAiOTklIikgJT4lIHBsb3QobWFpbj0iRGV0ZXJtaW5hbnQ6IDElIGhpZ2hlc3QgdmFsdWVzIChncmFkaWVudCkiKQpgYGAKCgoKYGBge3IgZWNobz1UUlVFfQpsYWIxIDwtIHRocmVzaG9sZChncmFkLm1hZywgIjk5JSIpICU+JSBsYWJlbApsYWIyIDwtIHRocmVzaG9sZCh3aXRoKGltaGVzc2lhbihwYXBTbWVhciksKHh4Knl5IC0geHleMikpLCAiOTklIikgJT4lIGxhYmVsCnBhcihtZnJvdz1jKDEsMikpCnBsb3QobGFiMSwgbWFpbj0gIkxhYmVsbGVkIHJlZ2lvbnMgZnJvbSBncmFkaWVudCIpCnBsb3QobGFiMiwgbWFpbj0gIkxhYmVsbGVkIHJlZ2lvbnMgZnJvbSBzY2FyZSIpCgpgYGAKCmBgYHtyfQpjbGFzcyhsYWIxKQpsYWIxCmBgYAoKYGBge3J9CmRmMSA8LSBhcy5kYXRhLmZyYW1lKGxhYjEpICU+JSBzdWJzZXQodmFsdWU+MCkKZGYyIDwtIGFzLmRhdGEuZnJhbWUobGFiMikgJT4lIHN1YnNldCh2YWx1ZT4wKQpoZWFkKGRmMiwzKQpgYGAKCgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKY2VudGVyczEgPC0gZHBseXI6Omdyb3VwX2J5KGRmMSx2YWx1ZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2UobXg9bWVhbih4KSxteT1tZWFuKHkpKQpwbG90KGxhYjEpCndpdGgoY2VudGVyczEsIHBvaW50cyhteCwgbXksIGNvbD0ieWVsbG93IikpCgpjZW50ZXJzMiA8LSBkcGx5cjo6Z3JvdXBfYnkoZGYyLHZhbHVlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShteD1tZWFuKHgpLG15PW1lYW4oeSkpCnBsb3QobGFiMikKd2l0aChjZW50ZXJzMiwgcG9pbnRzKG14LCBteSwgY29sPSJyZWQiKSkKYGBgCgoKYGBge3J9CmdldC5jZW50ZXJzIDwtIGZ1bmN0aW9uKGltLHRocj0iOTklIikKeyAgIGltIDwtIGlzb2JsdXIoaW0sNSkKICAgIGR0IDwtIGltaGVzc2lhbihpbSkgJSQlIHsgeHgqeXkgLSB4eV4yIH0gJT4lIHRocmVzaG9sZCh0aHIpICU+JSBsYWJlbAogICAgYXMuZGF0YS5mcmFtZShkdCkgJT4lIHN1YnNldCh2YWx1ZT4wKSAlPiUgZHBseXI6Omdyb3VwX2J5KHZhbHVlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShteD1tZWFuKHgpLG15PW1lYW4oeSkpCn0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChsYWIxLCBtYWluID0iIikKZ2V0LmNlbnRlcnMobGFiMSwiNTAlIikgJSQlIHBvaW50cyhteCxteSxjb2w9InllbGxvdyIpCnBsb3QoZ3JhZC5tYWcsbWFpbj0iR3JhZGllbnQgbWFnbml0dWRlIHdpdGggY29sb3IiKQpgYGAKCiMjIENvbmNsdXNpb24KQ2V0dGUgbcOpdGhvZGUgbmUgcGVybWV0IHBhcyBkZSBjb21wdGVyIGxlcyBjZWxsdWxlcy4KCiMjIEZhaXJlIGRlcyBlc3NheXMgYXZlYyBQaXhzZXRzCgpgYGB7cn0KaW0gPC0gbG9hZC5pbWFnZSgicGFwU21lYXIuanBnIikKaW0gPC0gZ3JheXNjYWxlKGltKQpweCA8LSBpbSA+IC42CnBhcihtZnJvdz1jKDEsMikpCnBsb3QoaW0sIG1haW4gPSAicmF3IGltYWdlIikKcGxvdChweCwgbWFpbiA9ICI+MC42IGludGVuc2l0eSIpCmRpbShweCkKZGltKGltKQptZWFuKHB4KQptZWFuKGltKQptZWFuKGltW3B4XSkKYGBgCgoKCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGltICwgbWFpbiA9ICJpbSIpCiNoaWdobGlnaHQoaW0pCnB4IDwtIGlzb2JsdXIoaW0sIDEpICA+IC42CnBsb3QocHgsIG1haW4gPSJweCIpCmhpZ2hsaWdodChweCkKYGBgCgpgYGB7cn0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChpbSkKI1N0YXJ0IHRoZSBmaWxsIGF0IGxvY2F0aW9uICgxODAsMjc0KS4gc2lnbWEgc2V0cyB0aGUgdG9sZXJhbmNlCnB4LmZsb29kKGltLDI3OCwzMDAsc2lnbWE9LjMxKSAlPiUgaGlnaGxpZ2h0CnNwIDwtIHNwbGl0X2Nvbm5lY3RlZChweCkKcGxvdChzcFsxOjRdKQpgYGAKCgoKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCiNUaGUgYm91bmRhcnkgZnVuY3Rpb24gY29tcHV0ZXMgdGhlIGJvdW5kYXJpZXMgb2YgdGhlIHNldDoKYm91bmRhcnkocHgpICU+JSBwbG90KG1haW4gPSAicHggPC0gaXNvYmx1cihpbSwgMSkgID4gLjYiKQpib3VuZGFyeShpbSkgJT4lIHBsb3QoIG1haW49ICJpbSIpCmBgYAoKCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGltLCBtYWluPSAiaW0gd2l0aCBib3VuZGFyeSIpCiMjI01ha2UgeW91ciBvd24gaGlnaGxpZ2h0IGZ1bmN0aW9uOgpib3VuZGFyeShweCkgJT4lIHdoZXJlICUkJSB7IHBvaW50cyh4LHksY2V4PS4xLGNvbD0icmVkIikgfQpwbG90KGltLCBtYWluPSJpbSB3aXRoIGhpZ2hsaWdodChweCkiKQpoaWdobGlnaHQocHgpCmBgYAoKCgpgYGB7cn0KIyMgVGhlIGdyb3cgYW5kIHNocmluayBvcGVyYXRvcnMgbGV0IHlvdSBncm93IGFuZCBzaHJpbmsgcGl4c2V0cyB1c2luZyBtb3JwaG9sb2dpY2FsIG9wZXJhdG9ycyAoZGlsYXRpb24gYW5kIGVyb3Npb24sIHJlc3AuKQojcGFyKG1mcm93PWMoMSwyKSkKcGxvdChpbSwgbWFpbj0id2l0aCBzaGluayIpCmhpZ2hsaWdodChweCkKI1NocmluayBieSA1IHBpeGVscwpzaHJpbmsocHgsNSkgJT4lIGhpZ2hsaWdodChjb2w9ImJsdWUiKQojR3JvdyBieSA1IHBpeGVscwpncm93KHB4LDUpICU+JSBoaWdobGlnaHQoY29sPSJncmVlbiIpCiNDb21wdXRlIGJvdW5kaW5nIGJveApiYm94KHB4KSAlPiUgaGlnaGxpZ2h0KGNvbD0ieWVsbG93IikKYGBgCgoKYGBge3J9CnB4Lm5vbmUoaW0pICNObyBwaXhlbHMKcHguYWxsKGltKSAjQWxsIG9mIHRoZW0KYGBgCgpgYGB7cn0KcGxvdChpbSkKI0ltYWdlIGJvcmRlcnMgYXQgZGVwdGggMTAKcHguYm9yZGVycyhpbSwxMCkgJT4lIGhpZ2hsaWdodAojTGVmdC1oYW5kIGJvcmRlciAoNSBwaXhlbHMpLCBzZWUgYWxzbyBweC50b3AsIHB4LmJvdHRvbSwgZXRjLgpweC5sZWZ0KGltLDUpICU+JSBoaWdobGlnaHQoY29sPSJncmVlbiIpCmBgYAoKCgpgYGB7cn0KI1NwbGl0IHBpeHNldCBpbiB0d28gYWxvbmcgeAppbXNwbGl0KGltLCJ4IiwyKSAlPiUgcGxvdChsYXlvdXQ9InJvdyIpCmBgYAoKCmBgYHtyfQpwbG90KGltID4gLjYpCmBgYAoKYGBge3J9CiMgU3BsaXR0aW5nIGJ5IGNvbG9yCmltYyA8LSBsb2FkLmltYWdlKCJwYXBTbWVhci5qcGciKQppbXNwbGl0KGltYywiYyIpICU+JSBwbG90CmBgYAoKYGBge3J9CnBsb3QoaW1jKQppbXNwbGl0KGltYyA+IDAuNzUsImMiKSAlPiUgcGFyYW55ICU+JSBoaWdobGlnaHQKYGBgCgpgYGB7cn0KdGhyZXNob2xkKGltYykgJT4lIHBsb3QKYGBgCgpgYGB7cn0KbGlicmFyeShkcGx5cikKZCA8LSBhcy5kYXRhLmZyYW1lKGltKQojI1N1YnNhbWJsZSwgZml0IGEgbGluZWFyIG1vZGVsCm0gPC0gc2FtcGxlX24oZCwxZTQpICU+JSBsbSh2YWx1ZSB+IHgqeSxkYXRhPS4pCiMjQ29ycmVjdCBieSByZW1vdmluZyB0aGUgdHJlbmQKaW0uYyA8LSBpbS1wcmVkaWN0KG0sZCkKb3V0IDwtIHRocmVzaG9sZChpbS5jKQpwbG90KG91dCkKCm91dCA8LSBjbGVhbihvdXQsMykgJT4lIGltYWdlcjo6ZmlsbCg3KQpwbG90KGltKQpoaWdobGlnaHQob3V0KQpgYGAKCmBgYHtyfQpiZyA8LSAoIXRocmVzaG9sZChpbS5jLCIxMCUiKSkKZmcgPC0gKHRocmVzaG9sZChpbS5jLCI5MCUiKSkKaW1saXN0KGZnLGJnKSAlPiUgcGxvdChsYXlvdXQ9InJvdyIpCmBgYAoK